package utils;

import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

public class LambdaUtils
{

	public static <T, U, X, Y> Map<X, Y> map(Map<U, T> map, Function<U, X> key, Function<T, Y> value)
	{
		Map<X, Y> newmap = new HashMap<>();
		map.forEach((k, v) -> {
			newmap.put(key.apply(k), value.apply(v));
		});
		return newmap;
	}

	public static <T, U, X> Map<X, T> mapKey(Map<U, T> map, Function<U, X> key)
	{
		return map(map, key, x -> x);
	}

	public static <T, U, Y> Map<U, Y> mapValue(Map<U, T> map, Function<T, Y> value)
	{
		return map(map, x -> x, value);
	}

	public static <T, U> Map<U, T> mapFilter(Map<U, T> map, Predicate<Entry<U, T>> keyExtractor)
	{
		return list2map2(filter(map.entrySet(), keyExtractor), x -> x.getKey(), x -> x.getValue());
	}

	public static <T> List<T> flatMap(List<List<T>> list)
	{
		return list.stream().flatMap(Collection::stream).collect(Collectors.toList());
	}

	public static <T> List<T> flatMap(Collection<Collection<T>> list)
	{
		return list.stream().flatMap(Collection::stream).collect(Collectors.toList());
	}

	// list 转 map，指定一个属性当key，value默认U
	public static <T, U> Map<T, U> list2map2(Collection<U> list, Function<U, T> key)
	{
		return list2map2(list, key, x -> x);
	}

	// list 转 map，指定一个属性当key，执行一个属性当value
	public static <U, T, K> Map<T, K> list2map2(Collection<U> list, Function<U, T> key, Function<U, K> value)
	{
		return list.stream().collect(Collectors.toMap(key, value, (key1, key2) -> key2, LinkedHashMap::new));
	}

	// list抽取属性
	public static <U, T> List<T> list2list(Collection<U> list, Function<U, T> fun)
	{
		return list.stream().map(fun).collect(Collectors.toList());
	}

	// list抽取属性
	public static <U, T, X> void list4list(List<U> sources, Function<U, T> sourcekey, List<X> dests, Function<X, T> destkey, BiConsumer<X, U> action)
	{
		Map<T, U> sourceMap = list2map2(sources, sourcekey);
		dests.forEach(item -> {
			U u = sourceMap.get(destkey.apply(item));
			action.accept(item, u);
		});

	}

	public static <U> JSONArray list2JSONArray(Collection<U> list, Function<U, JSONObject> fun)
	{
		JSONArray arr = new JSONArray();
		list.forEach(item -> {
			arr.add(fun.apply(item));
		});
		return arr;
	}

	public static <U, T> JSONArray list2JSONArraySimple(Collection<U> list, Function<U, T> fun)
	{
		JSONArray arr = new JSONArray();
		list.forEach(item -> {
			arr.add(fun.apply(item));
		});
		return arr;
	}

	public static <U, T> Map<T, U> JSONArray2map(JSONArray array, Function<JSONObject, T> key, Function<JSONObject, U> value)
	{
		Map<T, U> result = list2map2(array, x -> {
			JSONObject json = (JSONObject) x;
			return key.apply(json);
		}, x -> {
			JSONObject json = (JSONObject) x;
			return value.apply(json);
		});
		return result;
	}

	// 要注意：这个方法生成的结果好像是拷贝的，和原来的没有关系，如果用原来的对象，自己写吧
	@Deprecated
	public static <T> List<T> JSONArray2list(JSONArray array, Class<T> clazz)
	{
		return JSONObject.parseArray(array.toJSONString(), clazz);
	}

	// 任意属性排序
	public static <T, U> void order(List<T> list, Comparator<U> c, Function<T, U> keyExtractor)
	{
		list.sort(Comparator.comparing(keyExtractor, c));
	}

	// 过滤
	public static <U> List<U> filter(Collection<U> list, Predicate<U> keyExtractor)
	{
		return list.stream().filter(keyExtractor).collect(Collectors.toList());
	}

	// groupby
	public static <T, U> Map<U, List<T>> groupby(List<T> list, Function<T, U> keyExtractor)
	{
		return list.stream().collect(Collectors.groupingBy(keyExtractor, LinkedHashMap::new, Collectors.toList()));
	}

	public static <T, X, U> Map<U, List<T>> groupbywhole(List<T> list, List<X> groups1, Function<X, U> keyExtractor, Function<X, Predicate<T>> value2Predicate)
	{
		return LambdaUtils.list2map2(groups1, keyExtractor, x -> {
			return LambdaUtils.filter(list, value2Predicate.apply(x));
		});
	}

	public static <T, U, K> Map<U, Map<K, T>> groupby(List<T> list, Function<T, U> groupExtractor, Function<T, K> keyExtractor)
	{
		return list.stream().collect(Collectors.groupingBy(groupExtractor, LinkedHashMap::new, Collectors.toMap(keyExtractor, x -> x, (key1, key2) -> key2, LinkedHashMap::new)));
	}

	public static <T, U, K, X> Map<U, Map<K, X>> groupby(List<T> list, Function<T, U> groupExtractor, Function<T, K> keyExtractor, Function<T, X> valueExtractor)
	{
		return list.stream().collect(Collectors.groupingBy(groupExtractor, LinkedHashMap::new, Collectors.toMap(keyExtractor, valueExtractor, (key1, key2) -> key2, LinkedHashMap::new)));
	}

	public static <T, U, K> Map<U, Map<K, List<T>>> groupby2(List<T> list, Function<T, U> groupExtractor, Function<T, K> keyExtractor)
	{
		return list.stream().collect(Collectors.groupingBy(groupExtractor, LinkedHashMap::new, Collectors.groupingBy(keyExtractor, LinkedHashMap::new, Collectors.toList())));
	}

	public static <T, U, K, X, Y> Map<K, Map<Y, List<T>>> groupby2whole(List<T> list, List<U> groups1, Function<U, K> key1Fun, Function<U, Predicate<T>> value1Predicate, List<X> groups2,
			Function<X, Y> key2Fun, Function<X, Predicate<T>> value2Predicate)
	{
		Map<K, Map<Y, List<T>>> result = LambdaUtils.list2map2(groups1, key1Fun, x -> {
			List<T> ts = LambdaUtils.filter(list, value1Predicate.apply(x));
			Map<Y, List<T>> item = LambdaUtils.list2map2(groups2, key2Fun, y -> {
				return LambdaUtils.filter(ts, z -> value2Predicate.apply(y).test(z));
			});
			return item;
		});
		return result;
	}

	public static <T, U, K, X, P, Y> Map<K, Map<Y, List<T>>> groupby2whole(List<T> list, List<U> groups1, Function<U, K> key1Fun, Function<U, Predicate<T>> value1Predicate, List<X> groups2,
			Function<X, Y> key2Fun, Function<X, P> value2Fun, Function<T, P> value2tFun)
	{
		return LambdaUtils.groupby2whole(list, groups1, key1Fun, value1Predicate, groups2, key2Fun, x -> t -> value2tFun.apply(t) == value2Fun.apply(x));
	}

	public static <T, U, K, A> Map<U, Map<K, Map<A, T>>> groupby2map(List<T> list, Function<T, U> firstKey, Function<T, K> secondKey, Function<T, A> mapKey)
	{
		return list.stream().collect(
				Collectors.groupingBy(firstKey, LinkedHashMap::new, Collectors.groupingBy(secondKey, LinkedHashMap::new, Collectors.toMap(mapKey, x -> x, (key1, key2) -> key2, LinkedHashMap::new))));
	}

	public static <T, U, K> Map<U, K> groupby(Collection<T> list, Function<T, U> groupExtractor, Collector<T, ?, K> c)
	{
		return list.stream().collect(Collectors.groupingBy(groupExtractor, LinkedHashMap::new, c));
	}

	public static <T, U, K, R> Map<U, Map<K, R>> groupby2(Collection<T> list, Function<T, U> groupExtractor, Function<T, K> keyExtractor, Collector<T, ?, R> c)
	{
		return list.stream().collect(Collectors.groupingBy(groupExtractor, LinkedHashMap::new, Collectors.groupingBy(keyExtractor, LinkedHashMap::new, c)));
	}

	public static <T, U, X> Map<U, T> whole4group(Map<U, T> map, List<X> list, Function<X, U> fun, T defaultt)
	{
		Map<U, T> newmap = new LinkedHashMap<>();
		for (X x : list)
		{
			U u = fun.apply(x);
			if (map.containsKey(u))
			{
				newmap.put(u, map.get(u));
			}
			else
			{
				newmap.put(u, defaultt);
			}
		}
		return newmap;
	}
}
